<?php
/**
 * DMFramework
 * 
 * @license Copyright 2012 Dillen Meijboom under the Apache license version 2.0 (http://apache.org/licenses/LICENSE-2.0)
 * @author Dillen Meijboom <info@dillenm.nl>
 * @url http://dmframework.nl
 */

/**
 * The router class can load a controller and start the page-rendering process
 * being done by the Controller self.
 * 
 * @license Copyright 2012 Dillen Meijboom under the Apache license version 2.0 (http://apache.org/licenses/LICENSE-2.0)
 * @author Dillen Meijboom <info@dillenm.nl>
 * @package System.Core
 */
class Dmf_Router
{
    /**
     * All rewrite rules
     * 
     * @var array 
     */
    private $_rewriteRules = array();
    
    /**
     * All custom routs
     * 
     * @var array
     */
    private $_routes = array();
    
    /**
     * The request library
     * 
     * @var Dmf_Request
     */
    private $_request;
    
    /**
     * The template library
     * 
     * @var Dmf_Template
     */
    private $_template;
    
    /**
     * Configuration as object
     * 
     * @var Object
     */
    private $_config;
    
    /**
     * The bootstral class
     * 
     * @var Dmf_Bootstrap
     */
    private $_bootstrap;
    
    /**
     * Constructor
     * 
     * @param Dmf_Request $request
     * @throws Exception 
     */
    public function __construct( $bootstrap = null )
    {
        $this->_config = Dmf_Application::getInstance()->getConfig()->asObject();
        $this->_bootstrap = $bootstrap;
    }
    
    /**
     * Load a rules file
     * 
     * @param string $filename 
     */
    public function loadRulesFile( $filename )
    {        
        if ( ! file_exists( $filename ) )
        {
            throw new Dmf_Exception_FileNotFound( 'The rules file: "' . $filename . '" could not be found.' );
        }
        
        $lines = explode( PHP_EOL, file_get_contents( $filename ) );

        foreach( $lines as $line )
        {
            //Filter \r for windows-style files
            $parts = explode( ' ', str_replace( "\r", '', $line ) );

            if ( $parts['0'] == 'Rewrite' )
            {
                $this->_rewriteRules[] = array( $parts['1'], $parts['2'] );
            }

            if ( $parts['0'] == 'Block' && $_SERVER['REMOTE_ADDR'] == $parts['1'] )
            {
                throw new Exception( 'Your ip-address has been blocked.' );
            }
        }
    }
    
    /**
     * Add custom route
     * 
     * @param string $url
     * @param Dmf_Request $request
     * @param boolean|string $action 
     */
    public function addRoute( $url, $request )
    {
        $this->_routes[] = array(
            'url' => $url,
            'request' => $request
        );
    }
    
    /**
     * Load a controller
     * 
     * @param array $customDispatchArray
     */
    private function _loadController()
    {
        //Load controller and action
        try
        {
            $controller = $this->_request->getController() . 'Controller';
            $controller = new $controller( $this->_request, $this->_template );
            
            $action = $this->_request->getAction() . 'Action';
        }
        catch( Dmf_Exception_ClassNotFound $e )
        {
            if ( ! $controller instanceof Dmf_Application_Controller && class_exists( $this->_request->getController() . 'Controller', false ) )
            {
                throw new Exception( 'The controller does not extends: "Dmf_Application_Controller".' );
            }
            
            throw new Dmf_Exception_PageNotFound( 'Controller: "' . $this->_request->getController() . '" not found.' );
        }
        
        //Check if the required methods can be called
        if ( ! method_exists( $controller, $action ) )
        {
            if ( method_exists( $controller, $this->_request->getAction() ) )
            {
                throw new Dmf_Exception_PageNotFound( 'Controller: "' . $this->_request->getController() . '" found but action: "' . $this->_request->getAction() . '" is missing (don\'t forget the "Action" prefix).' );
            }
            
            throw new Dmf_Exception_PageNotFound( 'Controller: "' . $this->_request->getController() . '" found but action: "' . $this->_request->getAction() . '" is missing.' );
        }
        
        //Call the controller initiate method
        $controller->__init();
        
        //When there are no parameters direct call is way faster
        if ( count( $this->_request->getParameters() ) == 0 )
        {
            $result = $controller->$action();
        }
        else
        {
            $result = call_user_func_array( array( $controller, $action ), $this->_request->getParameters() );
        }
        
        $this->_template->layout_file = $controller->layout_file;
        
        if ( $this->_config->view->autoload ) 
        {
            $this->_template->renderPage( $result );
        }
        else
        {
            $this->_template->renderContent( $result );
        }
    }
    
    /**
     * Get all rewrite rules
     * 
     * @return array
     */
    public function getRewriteRules()
    {
        return $this->_rewriteRules;
    }
    
    /**
     * Dispatch the router
     * 
     * @param Dmf_Request $request
     * @throws Exception
     * @throws Dmf_Exception_PageNotFound 
     */
    public function dispatch( Dmf_Request $request )
    {
        $requestString = $request->getRequest();
        $this->_request = $request;
        
        foreach( $this->_routes as $route )
        {
            if ( $route['url'] == $requestString )
            {
                $requestString = $route['request']->getRequest();
            }
        }
        
        //If the requestString has been changed
        if ( $requestString != $request->getRequest() )
        {
            $this->_request->recalculate( $requestString );
        }
        
        //Create template instance
        $this->_template = new Dmf_Template( $this->_request );
        
        //Prepare full output page
        if ( $this->_config->view->autoload ) $this->_template->preparePage();
        
        //Call the template rendered method of the bootstrap
        if ( $this->_bootstrap instanceof Dmf_Application_Bootstrap )
        {
            $this->_bootstrap->__templateRendered( $this->_template->layout, $this->_template->view );
        }
        
        //Load the controller
        $this->_loadController();
    }
}